GH GambleHub

პაგინაცია და კურსორები

1) რატომ არის საჭირო პაგინაცია

პაგინაცია ზღუდავს კლიენტის მიერ გადაცემული და გადაკეთებული მონაცემების მოცულობას, ამცირებს დატვირთვას შენახვის/ქსელებზე და ადგენს დეტერმინის მეთოდს „გასეირნება“ კოლექციის მიხედვით. რეალურ სისტემებში პაგინაცია არ არის მხოლოდ 'page = 1 & limit = 50', არამედ პროტოკოლის ხელშეკრულებების ერთობლიობა და თანმიმდევრულობის ინვარიანტები.

ტიპიური მიზნები:
  • ლატენტობის კონტროლი და მოთხოვნის მეხსიერება.
  • სტაბილური ნავიგაცია მონაცემთა ნაკრების შეცვლისას (ჩანართი/წაშლა).
  • ადგილიდან განახლების შესაძლებლობა.
  • კეშინგი და წინასწარი დატვირთვა.
  • ბოროტად გამოყენებისგან დაცვა.

2) პაგინაციის მოდელები

2. 1 OFFSET/LIMIT (გვერდი)

იდეა: „გამოტოვეთ N ხაზები, დააბრუნეთ M“.
დადებითი: სიმარტივე თავსებადია თითქმის ნებისმიერი SQL/NoSQL.

უარყოფითი:
  • ხაზოვანი დეგრადაცია: დიდი OFFSET იწვევს სრულ სკანირებას/skip-cost.
  • არასტაბილურობა მოთხოვნებს შორის ჩანართების/მოცილების დროს (გადაადგილება „ბანაობა“).
  • ძნელია ზუსტი „განახლებადობის“ უზრუნველყოფა.
SQL მაგალითი:
sql
SELECT
FROM orders
ORDER BY created_at DESC, id DESC
OFFSET 1000 LIMIT 50;

2. 2 Cursor/Keyset/Seek პაგინაცია

იდეა: „გააგრძელე გასაღები K“. კურსორი არის პოზიცია დალაგებულ ნაკრებში.

დადებითი:
  • O (1) ინდექსის თანდასწრებით გაგრძელება.
  • სტაბილურობა კოლექციის ცვლილებებში.
  • საუკეთესო ლატენტობა ღრმა „გვერდებზე“.
უარყოფითი:
  • ჩვენ გვჭირდება მკაცრად განსაზღვრული, უნიკალური და ერთფეროვანი დახარისხების გასაღებები.
  • განხორციელება და გამართვა უფრო რთულია.
SQL მაგალითი (seek):
sql
-- Resumption after steam (created_at, id) = (:last_ts,:last_id)
SELECT
FROM orders
WHERE (created_at, id) < (:last_ts,:last_id)
ORDER BY created_at DESC, id DESC
LIMIT 50;

2. 3 Continuation tokens (გაუმჭვირვალე ნიშნები)

იდეა: სერვერი უბრუნდება opaque ნიშანს, რომელშიც დაშიფრულია „პოზიცია“ (და, შესაძლოა, შარდების/ფილტრების მდგომარეობა). კლიენტს არ ესმის ინტერიერი და უბრალოდ ბრუნდება ნიშანი შემდეგი გვერდისთვის.
დადებითი: მოქნილობა, სქემის შეცვლის შესაძლებლობა API დარღვევის გარეშე.
უარყოფითი მხარეები: ტოქსინების სიცოცხლის ხანგრძლივობის კონტროლი, მოქმედებების თავსებადობა.

2. 4 დროებითი და ლოგიკური კურსორები

Time-based: „ყველა ჩანაწერი T“, კურსორი - დროის ეტიკეტი (შესაფერისია append-only ნაკადებით).
Log-sequence/offset-based: კურსორი - გადაადგილება ლოგოში (Kafka offset, journal seq).
Global monotonic IDs: Snowflake/UUIDv7, როგორც სტაბილური seek- ის გასაღებები.

3) კურსებისა და ნიშნების დიზაინი

3. 1 კარგი კურსორის თვისებები

გაუმჭვირვალე: კლიენტი არ არის დამოკიდებული ფორმატით.
ავტორიტეტი/მთლიანობა: HMAC ხელმოწერა ჩანაცვლების/მანიპულირების თავიდან ასაცილებლად.
კონტექსტი: მოიცავს დახარისხებას, ფილტრებს, სქემის ვერსიას, tenant/shard.
სიცოცხლის ხანგრძლივობა: TTL და „შეუსაბამობა“ ინდექსების/წვდომის უფლებების შეცვლისას.
ზომა: კომპაქტური (<= 1-2 KB), შესაფერისი URL- სთვის.

3. 2 ნიშნის ფორმატი

რეკომენდებული დასტის: JSON - შეკუმშვა (zstd/deflate) - BaseeuRL URL HMAC.

დატვირთვის სტრუქტურა (მაგალითი):
json
{
"v": 3 ,//token version
"sort": ["created_at:desc","id:desc"],
"pos": {"created_at":"2025-10-30T12:34:56. 789Z","id":"987654321"},
"filters": {"user_id":"42","status":["paid","shipped"]},
"tenant": "eu-west-1",
"shards": [{"s ": "a, "" hi":"..."}] ,//optional: cross-shard context
"issued_at": "2025-10-31T14:00:00Z",
"ttl_sec": 3600
}

ზემოდან ემატება 'mac = HMAC (secret, payload)' და ყველაფერი დაშიფრულია ერთ სტრიქონში.

3. 3 უსაფრთხოება

ხელმოწერა (HMAC/SHA-256).
სურვილისამებრ დაშიფვრა (AES-GCM) მგრძნობიარე მნიშვნელობებით (PII).
სერვერზე შესაბამისობა: ვერსია, TTL, მომხმარებლის უფლებამოსილება (RBAC/ABAC).

4) კოორდინაცია და ინვარიანტები

4. 1 სტაბილური დახარისხება

გამოიყენეთ სრული დეტერმინიზმი: 'ORDER BY ts DESC, id DESC'.
დახარისხების გასაღები უნდა იყოს უნიკალური (დაამატეთ 'id' როგორც tiebreaker).
ინდექსი უნდა შეესაბამებოდეს დახარისხების ინდექსს.

4. 2 სურათები (snapshot) და იზოლაცია

„ღარიბი“ გვერდებისთვის გამოიყენეთ read-consistent snapshot (MVCC/txid).
თუ snapshot არაპრაქტიკულია (ძვირი/ბევრი მონაცემი), ჩამოაყალიბეთ კონტრაქტი: „კურსორი უბრუნებს ელემენტებს მკაცრად ადრე“. ეს ბუნებრივია ახალი ამბების ფილმებისთვის.

4. 3 ჩანართები/წაშლა გვერდებს შორის

Seek მოდელი მინიმუმამდე ამცირებს „დუბლიკატებს/უღელტეხილებს“.
ამოიღეთ ქცევა წაშლის/შეცვლის დროს: იშვიათი „ხვრელები“ დაიშვება გვერდებს შორის, მაგრამ არა „დროულად უკან“.

5) იდენტიფიკატორის ინდექსირება და სქემები

კომპოზიციური ინდექსები მკაცრად დალაგების თანმიმდევრობით: '(created _ at DESC, id DESC)'.
მონოტონური ID: Snowflake/UUIDv7 დროულად ბრძანებს და აჩქარებს seek- ს.
ცხელი გასაღებები: განაწილეთ shard-key (მაგალითად, 'tenant _ id', 'region') და მოათავსეთ shard შიგნით.
ID გენერატორები: თავიდან აიცილეთ კონფლიქტები და „საათების მომავლიდან“ - დროის სინქრონიზაცია, NTP ნახტომი „რეგრესია“.

6) ჯვარედინი შარდის პაგინაცია

6. 1 სქემები

Scatter-Gather: პარალელური მოთხოვნები ყველა შარდში, ადგილობრივი seek კურსები, შემდეგ k-way merge აგრეგატორზე.
Per-Shard Cursors: ნიშანი შეიცავს პოზიციებს თითოეულ ხიბლზე.
Bounded fan-out: შეზღუდეთ ხარდიების რაოდენობა ერთი ნაბიჯით.

6. 2 ნიშანი მრავალფუნქციური

შეინახეთ მასივი '{shard _ id, last _ pos'. შემდეგ ეტაპზე განაახლეთ თითოეული აქტიური ხიბლისთვის და კვლავ მოინათლეთ, გლობალურად დალაგებული გვერდის მიცემით.

7) პროტოკოლის კონტრაქტები

7. 1 REST

მოთხოვნა:

GET /v1/orders? limit=50&cursor=eyJ2IjoiMyIsInNvcnQiOiJjcmVh... (opaque)
პასუხი:
json
{
"items": [ /... / ],
"page": {
"limit": 50,
"next_cursor": "eyJ2IjozLCJwb3MiOiJjcmVh...==",
"has_more": true
}
}
რეკომენდაციები:
  • „ლიმიტი“ ზედა ზღვარით (მაგალითად, max = 200).
  • 'შემდეგი _ cursor' არ არსებობს, თუ 'has _ more = false'.
  • GET idempotention, პასუხის ქეშირება 'შემდეგი _ cursor' გარეშე (პირველი გვერდი ფიქსირებული ფილტრებით და snapshot).

7. 2 GraphQL (Relay მიდგომა)

ტიპიური კონტრაქტი „კავშირი“:
graphql type Query {
orders(first: Int, after: String, filter: OrderFilter): OrderConnection!
}

type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
}

type OrderEdge {
node: Order!
cursor: String! // opaque
}

type PageInfo {
hasNextPage: Boolean!
endCursor: String
}

'cursor' უნდა იყოს გაუმჭვირვალე და ხელმოწერილი; არ გამოიყენოთ „ნედლეული Base64 (id)“ HMAC- ის გარეშე.

7. 3 gRPC

გამოიყენეთ 'page _ size' და 'page _ token':
proto message ListOrdersRequest {
string filter = 1;
int32 page_size = 2;
string page_token = 3; // opaque
}

message ListOrdersResponse {
repeated Order items = 1;
string next_page_token = 2; // opaque bool has_more = 3;
}

7. 4 ნაკადები და WebSockets

უწყვეტი ფირებისთვის: კურსორი, როგორც „ბოლო ნახული offset/ts“.

შეინარჩუნეთ 'resume _ from' რეკონსტრუქციის დროს:
json
{ "action":"subscribe", "topic":"orders", "resume_from":"2025-10-31T12:00:00Z#987654321" }

8) შეშუპება, წინასწარ დატვირთვა, CDN

ETag/If-None-Match პირველი გვერდისთვის სტაბილური ფილტრებით.
Cache Control მოკლე TTL (მაგალითად, 5-30 გვ) საზოგადოებრივი სიებისთვის.
Prefetch: დაუბრუნეთ 'შემდეგი _ cursor' და რჩევები ('Link: rel = „შემდეგი“), კლიენტს შეუძლია შემდეგი გვერდის გადატვირთვა.
ვარიაციები: გაითვალისწინეთ 'filter/sort/locale/tenant', როგორც ქეშის ნაწილის გასაღები.

9) ტვირთის მართვა და შეზღუდვა

„ლიმიტის“ ზედა საზღვარი, მაგალითად 200.
Server side backpressure: თუ მოთხოვნის დრო> budget, შეამცირეთ 'limit' საპასუხოდ (და აშკარად აცნობეთ კლიენტს გვერდის ფაქტობრივი ზომა).
მომხმარებლის ლიმიტები/ნიშანი/ტენანტი.
Timeout/Retry: ექსპონენციალური პაუზა, immpotent განმეორებითი მოთხოვნები.

10) UX ასპექტები

ნაკაწრი ნუმერაციის საწინააღმდეგოდ: გაუთავებელი გადახრა, კურსორები; offset სალიცენზიო გვერდები (მაგრამ ახსენით უზუსტობა მონაცემთა განახლებისას).
ღილაკი „დაბრუნდით ადგილზე“: შეინახეთ კლიენტის კურსორების დასტური.
ცარიელი გვერდები: თუ 'has _ more = false', არ აჩვენოთ ღილაკი „სხვა“.
სტაბილური საზღვრები: აჩვენეთ ზუსტი 'ტოტალური' მხოლოდ იმ შემთხვევაში, თუ ის იაფია (წინააღმდეგ შემთხვევაში - სავარაუდო 'approx _ total').

11) ტესტირება და edge შემთხვევები

ჩეკის ფურცლები:
  • სტაბილური დახარისხება: იგივე 'ts' ელემენტები არ არის „მოციმციმე“.
  • ჩანართები/წაშლა: განმეორებები არ ჩანს გვერდების კვანძზე.
  • ფილტრების შეცვლა გვერდებს შორის: ნიშანი უნდა გადახრა, როგორც „მოძველებული/შეუთავსებელი“.
  • TTL ნიშანი: სწორი შეცდომა ვადის გასვლის შემდეგ.
  • დიდი სიღრმე: ლატენტობა ხაზოვანი არ იზრდება.
  • მულტიშარდი: სწორი მერჯიანი წესრიგი, „ნელი“ ხარდიების არარსებობა.
მაგალითი property-based ცომი (ფსევდო კოდი):
python
Generate N entries with random inserts between calls
Verify that all pages are merged = = whole ordered fetch

12) დაკვირვება და SLO

მეტრიკა:
  • 'list _ request _ latence _ ms' (P50/P95/P99) გვერდის სიგრძით.
  • 'seek _ index _ hit _ ratio' (მოთხოვნების წილი, რომლებიც ტოვებენ ინდექსს).
  • 'შემდეგი _ cursor _ invalid _ rate' (ვალიდაციის შეცდომები/TTL/ხელმოწერები).
  • 'merge _ fanout' (გვერდზე ჩართული ხუმრობების რაოდენობა).
  • 'duplicates _ on _ boundary' და 'gaps _ on _ boundary' (დეტალი მომხმარებლის ტელემეტრიის შესახებ).
Logs/Tracing:
  • დაუკავშირეთ 'cursor _ id' ლოგებში, შენიღბეთ payload.
  • Spage spans: 'page _ size', 'source _ shards', 'db _ index _ used'.
SLO მაგალითი:
  • წვდომა: 99. 9% 'List' მეთოდებზე.
  • ლატენტობა: P95 <200 ms 'page _ size <= 50' ადგილობრივი ხარის ქვეშ.
  • ნიშნის შეცდომა: <0. ზარების საერთო რაოდენობის 1%.

13) მიგრაცია და თავსებადობა

ჩართეთ „v“ ტოკენში და მხარი დაუჭირეთ N კვირის ძველ ვერსიებს.
დახარისხების ღილაკების შეცვლისას, გაგზავნეთ „რბილი“ შეცდომა '409 Conflict' ახალი ჩამონათვალის გარეშე.
კატასტროფული შემთხვევა (ყველა ნიშნის ჭრილობა): შეცვალეთ 'signing _ key _ id' და გადახრა ძველი.

14) განხორციელების მაგალითები

14. 1 ნიშნის თაობა (ფსევდო კოდი)

python payload = json. dumps({...}). encode()
compressed = zlib. compress(payload)
mac = hmac_sha256(signing_key, compressed)
token = base64url_encode(mac + compressed)

14. 2 ნიშნის მოქმედება

python raw = base64url_decode(token)
mac, compressed = raw[:32], raw[32:]
assert mac == hmac_sha256(signing_key, compressed)
payload = json. loads(zlib. decompress(compressed))
assert now() - payload["issued_at"] < payload["ttl_sec"]
assert payload["filters"] == req. filters

14. 3 Seek მოთხოვნა კომპოზიციური გასაღებით

sql
-- Page # 1
SELECT FROM feed
WHERE tenant_id =:t
ORDER BY ts DESC, id DESC
LIMIT:limit;

-- Next pages, continued after (ts0, id0)
SELECT FROM feed
WHERE tenant_id =:t
AND (ts <:ts0 OR (ts =:ts0 AND id <:id0))
ORDER BY ts DESC, id DESC
LIMIT:limit;

15) უსაფრთხოება და შესაბამისობა

არ შეიტანოთ ნედლეული ველები, საიდანაც PII შეიძლება გამოვიდეს.
ხელი მოაწერეთ და შეზღუდეთ TTL.
შეეცადეთ გააკეთოთ ნიშნები მომხმარებლებს შორის შეუწყნარებელი (შეიტანეთ 'sub/tenant/roles' payload- ში და შეამოწმეთ ვალიდაციის დროს).
გააფართოვეთ მხოლოდ ტოქსინების ჰაშები.

16) ხშირი შეცდომები და საწინააღმდეგო ნიმუშები

Base64 (id) როგორც კურსორი: ადვილია გაყალბება/შერჩევა, არღვევს კონტრაქტს დახარისხების შეცვლისას.
tie-breaker- ის არარსებობა: 'ORDER BY ts DESC' გარეშე 'id' - დუბლიკატები/ნახტომი.
ნიშნის ინვალიდობის გარეშე გვერდებს შორის ფილტრების შეცვლა.
ღრმა OFFSET: ნელა და არაპროგნოზირებად.
ნიშნები ვერსიის გარეშე და TTL.

17) განხორციელების მინი-ჩეკლისტი

1. განსაზღვრეთ დახარისხება და დაამატეთ უნიკალური tie-breaker.
2. შექმენით ინდექსი, რომელიც მოიცავს ამ წესრიგს.
3. შეარჩიეთ მოდელი: seek + გაუმჭვირვალე ნიშანი.
4. განახორციელეთ ნიშნის ხელმოწერა (და, საჭიროების შემთხვევაში, დაშიფვრა).
5. განათავსეთ TTL და ვერსია.
6. ჩამოაყალიბეთ და შეაფასეთ კონტრაქტები 'has _ more', 'შემდეგი _ cursor'.
7. იფიქრეთ ჯვარედინი შარდის სქემაზე (საჭიროების შემთხვევაში) და k-way მერჯზე.
8. დაამატეთ მეტრიკები, ალერტები და SLO.
9. დაფარეთ გვერდის საზღვრის ტესტები.
10. აღწერეთ ტოქსინების მიგრაციის სტრატეგია.

18) მოკლე რეკომენდაციები მიდგომის არჩევის შესახებ

კატალოგები/ძებნა, სადაც მნიშვნელოვანია „გვერდის ნომერი“ და სავარაუდო ტოტალი: მაგალითად 'OFFSET/LIMIT' + ქეში; აცნობეთ, რომ ტოტალური სავარაუდოა.
ფირები, ანალიტიკა, ღრმა სიები, მაღალი RPS: მხოლოდ cursor/seek.
შარდენის/განაწილებული კოლექციები: per-shard cursors + merge ნიშანი.
ნაკადები/CDC: კურსორები, როგორც offsets/ts განახლებებით.

19) API შეთანხმების მაგალითი (რეზიუმე)

`GET /v1/items? limit=50&cursor=...`

პასუხი ყოველთვის მოიცავს 'page. limit`, `page. has _ more ', სურვილისამებრ' page. next_cursor`.
კურსორი არის გაუმჭვირვალე, ხელმოწერილი, TTL- ით.
დახარისხება დეტერმინირებულია: 'ORDER BY created _ at DESC, id DESC'.
ქცევა კომპლექტში ცვლილებების დროს: ელემენტები კურსორთან შედარებით არ ბრუნდებიან „უკან“.
მეტრიკა და შეცდომები სტანდარტიზებულია: 'invalid _ cursor', 'expired _ cursor', 'mismatch _ filters'.

ეს სტატია იძლევა არქიტექტურულ პრინციპებს და მზა ნიმუშებს პაგინაციის შესაქმნელად, რომელიც რჩება სწრაფი, პროგნოზირებადი და უსაფრთხო, თუნდაც დიდი მონაცემების პირობებში, შარდვა და ჩანაწერების აქტიურად ცვალებადი ნაკრები.

Contact

დაგვიკავშირდით

დაგვიკავშირდით ნებისმიერი კითხვის ან მხარდაჭერისთვის.ჩვენ ყოველთვის მზად ვართ დაგეხმაროთ!

ინტეგრაციის დაწყება

Email — სავალდებულოა. Telegram ან WhatsApp — სურვილისამებრ.

თქვენი სახელი არასავალდებულო
Email არასავალდებულო
თემა არასავალდებულო
შეტყობინება არასავალდებულო
Telegram არასავალდებულო
@
თუ მიუთითებთ Telegram-ს — ვუპასუხებთ იქაც, დამატებით Email-ზე.
WhatsApp არასავალდებულო
ფორმატი: ქვეყნის კოდი და ნომერი (მაგალითად, +995XXXXXXXXX).

ღილაკზე დაჭერით თქვენ ეთანხმებით თქვენი მონაცემების დამუშავებას.