~bigbes/tarantool-etcd · issues

997ma42ea5qf4oedqhpmhcccottoqj87 · 119 rows

idcontent_hashtitledescriptiondesignacceptance_criterianotesstatuspriorityissue_typeassigneeestimated_minutescreated_atcreated_byownerupdated_atclosed_atclosed_by_sessionexternal_refspec_idcompaction_levelcompacted_atcompacted_at_commitoriginal_sizesenderephemeralwisp_typepinnedis_templatemol_typework_typesource_systemmetadatasource_repoclose_reasonevent_kindactortargetpayloadawait_typeawait_idtimeout_nswaitershook_beadrole_beadagent_statelast_activityrole_typerigdue_atdefer_untilno_historystarted_atis_blocked
tarantool-etcd-sqve03dd24141b120acf0cc989ed396918ed61284c9962f1c036f30a262a51d39e6iproto-impl: etcd_election_proclaim vs Election.ProclaimWrap the existing handler election.proclaim (already invoked by gRPC/JSON via defs.lua route v3electionpb.Election.Proclaim) as a Lua global etcd_election_proclaim callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_election_proclaim', wrap('election', 'proclaim')) under the appropriate service block; append 'etcd_election_proclaim' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Election.Proclaim returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83.etcd_election_proclaim is callable from net.box:call('etcd_election_proclaim', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply.Epic tarantool-etcd-w8q. Handler at app/etcd/election.lua.open3taskNULLNULL2026-05-19T14:54:46ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:36ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-sy6ff088cd8c36175c942b8e80aa7a1bfa3bdaa64bf92a9ff46f8c736505ea0b302iproto-verify: etcd_watch_pull vs Watch.Watch-pullAdd Lua testkit verification for etcd_watch_pull (mirrors gRPC Watch.Watch-pull). Successful path: assert response shape matches the gRPC response message after MsgPack round-trip. Error path: at least one error case asserting {code, message} matches app/etcd/errors.lua catalog. Exercise at least one non-default option if applicable. Goes in test/iproto_parity_test.lua under group iproto/parity/watch. Call shape: local resp = etcd_watch_pull(req).Verification-only. Wrapper exists at app/etcd/api.lua. Epic tarantool-etcd-w8q.open3taskNULLNULL2026-05-19T14:53:57ZEugene Blikhbigbes@gmail.com2026-05-19T14:53:57ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-tf04410402a2773378d989b3453c6d566e9ab0eb85ec6a8ba9eb21801b478e96d73iproto-impl: etcd_maintenance_hash_kv vs Maintenance.HashKVWrap the existing handler maintenance.hash_kv (already invoked by gRPC/JSON via defs.lua route etcdserverpb.Maintenance.HashKV) as a Lua global etcd_maintenance_hash_kv callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_maintenance_hash_kv', wrap('maintenance', 'hash_kv')) under the appropriate service block; append 'etcd_maintenance_hash_kv' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Maintenance.HashKV returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83.etcd_maintenance_hash_kv is callable from net.box:call('etcd_maintenance_hash_kv', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply.Epic tarantool-etcd-w8q. Handler at app/etcd/maintenance.lua.open3taskNULLNULL2026-05-19T14:54:39ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:36ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-tt663510fbc9abb4568f1a181ec6515e1edededff7dbb3a0cf8e1f67e62b24c780dReplica route: lease / auth state mirroring (scope v1 vs later)KV is the v1 target; this issue scopes how non-KV state behaves in replica mode and what (if anything) v1 mirrors. Questions: - Leases: do we mirror upstream lease grants/TTLs/expiry so locally-served keys show correct lease attachment, or pass-through lease ops to upstream? At minimum the lease_id on replicated kvs must round-trip; full lease lifecycle mirroring may be later. - Auth: mirror upstream users/roles/permissions, or run replica reads unauthenticated/locally-authed? Decide whether auth is enforced on the replica or delegated. - Watch served to local clients: our Watch subsystem already reconstructs from kv_history, which the tail populates — confirm local watchers work transparently once KV sync lands. Output: a documented v1 scope (likely 'KV + lease_id round-trip only; auth+full-lease+ deferred') appended to docs/TODO.md, plus any follow-up issues.Likely mostly a scoping/decision issue that spawns concrete follow-ups; keep v1 KV-only.open2taskNULLNULL2026-05-20T06:17:57ZEugene Blikhbigbes@gmail.com2026-05-20T06:17:57ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-uee9e6ca4342149c79eee16a063f881ae3eb1683b9de5774eae58d2b37bfc8bcb9bReplica route: bootstrap snapshot importOn entering replica mode, pull the full upstream keyspace once and load it into our etcd_* spaces, recording the upstream revision the snapshot was taken at (the Watch tail starts from here). Approach: a clientv3-style Range over the whole keyspace ([\\x00, \\x00) with WithRange/WithFromKey) reading at a single revision (use the header.revision from the first Range, or RangeRequest with revision pinned + paging via WithLimit + WithMinModRev/last-key continuation). Persist each kv with its create_revision/mod_revision/version/lease intact. Alternatively consume Maintenance.Snapshot if/when interop lands (tarantool-etcd-16d) — but Range is the dependency-free path for v1. Output: etcd_* spaces populated, and the 'synced through revision R' watermark persisted for the tail phase to resume from.Must preserve upstream create_revision/mod_revision/version exactly — feeds revision-alignment. Page large keyspaces to avoid one giant Range.open2taskNULLNULL2026-05-20T06:17:54ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:37ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL1
tarantool-etcd-umcd8474c3796303d77baae0a958775f27a81b09ad2e0afaa4a00084cdf0ab6b7e9Learner: config block + role plumbingAdd the role config surface for learner mode and wire validate/apply/stop in app/roles/etcd.lua. New block under app.roles.etcd: learner: enabled: false cluster_peers: ['http://etcd-a:2380', ...] # PEER (2380) URLs, not client (2379) advertise_peer_url: 'http://this-node:2380' # how the cluster reaches us peer_tls: {...} Validate: when learner.enabled, the local store refuses normal client write registration; cluster_peers + advertise_peer_url non-empty; peer URLs are well-formed. No raft/transport logic here — just config parsing, validation, and the on/off switch the other phases hang off. Mirrors the existing grpc/json/cmux config validation in app/roles/etcd.lua.Distinct from the watch-mirror config (tarantool-etcd-jg3): peer (2380) URLs not client (2379), advertise_peer_url required for the cluster to dial us back.open2taskNULLNULL2026-05-20T06:45:13ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:36ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-unwf79d825d667d7493d1fbad53e65026978cea04ebcf650b8c77456f87f7698c30Tarantool metrics integrationRoadmap P0 blocker. Expose etcd-server operational metrics through Tarantool's metrics subsystem (request counts/latencies per RPC, watch/lease counts, revision, compaction, store size). Should surface via the standard Tarantool metrics collectors so existing TT monitoring (Prometheus exporter) picks them up. Consider parity with etcd's own /metrics names where it aids dashboards.Roadmap-only item; no metrics issue existed in beads.open0featureNULLNULL2026-05-20T06:55:23ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:36ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-vab80fa81f1f20e735f77e593b5e9975547a508502d5de68535ea41ac50091e1f7fAuthStatus must not require prior authentication (etcd 3.7)etcd 3.7: 'AuthStatus retrieval no longer requires prior authentication.' A client should be able to ask whether auth is enabled without already holding a token — which is the only sane ordering, since you need the answer to know whether to authenticate. Our JSON gateway gates every method except 'authenticate' behind a valid bearer token (app/etcd/json.lua:662), and the gRPC path uses the same predicate shape, so POST /v3/auth/status with no token returns 16 (Unauthenticated) once auth is enabled. Upstream 3.7 answers it. Fix: widen the exemption from a single method name to a small unauthenticated-method set containing 'authenticate' and 'status' (Auth.AuthStatus only — NOT Maintenance.Status, which routes to {'maintenance','status'} and shares the bare method name). Keep the gRPC and JSON gates driven by one predicate so they cannot drift.Match on the (service, method) pair, not the bare method string — routes.lua has both {'auth','status'} and {'maintenance','status'}. Check upstream 3.7 whether Maintenance.Status is also unauthenticated before changing it; this issue only claims Auth.AuthStatus.With auth enabled and no token: Auth.AuthStatus returns enabled=true + authRevision on both wires; every other method still returns Unauthenticated; a Lua test in test/auth_test.lua pins it.open3bugNULLNULL2026-07-17T11:30:22ZEugene Blikhbigbes@gmail.com2026-07-17T11:30:22ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-vnp1d9a61e1b6a987a8cb6ddae9833f7073ce29f05f3b852205dae442bfa24f3819Watch/Range reject compact_revision itself — off-by-one vs etcd, livelocks watch recoveryetcd keeps the tombstone whose revision == the compact revision and rejects only rev < compactMainRev (verified at server/storage/mvcc/kvstore_txn.go:80 — 'if rev < tr.s.compactMainRev'). A watch or range started exactly AT compact_revision succeeds. We reject at <= on both paths and delete the boundary row outright: - app/etcd/compact.lua:63-84 — the batch loop breaks on 'tuple.mod_revision > target_rev', so it deletes every kv_history row with mod_revision <= target_rev, INCLUDING the entry at target_rev. - app/etcd/watch.lua:281 — 'if opts.start_revision <= compacted then return errors.revision_compacted(compacted)' - app/etcd/kv.lua:261 — 'if target_rev <= compacted then error(errors.revision_compacted())' Why this is more than cosmetic: etcd's documented watch-recovery pattern is 'on a canceled response carrying compact_revision, restart the watch at compact_revision'. Against this server that revision is itself rejected, so a conforming clientv3 watcher that survives a compaction retries forever against the same rejection — an infinite cancel loop. Verified by hand (not just by the sweep agent) against both our source and the upstream clone.Two coupled changes; do them together or the boundary stays inconsistent: (1) compaction must retain rows with mod_revision == target_rev — break the loop on '>=' rather than deleting through the boundary; (2) both guards move from '<=' to '<'. Note the retained boundary row must be the tombstone/latest version at that revision, matching etcd's key_index.go behavior of keeping the generation's last entry.Compact(N) then Watch(start_revision=N) succeeds and delivers the event at N, matching etcd; Range(revision=N) after Compact(N) succeeds; a conformance test probes the compact-revision boundary itself on both backends (current compact_test.go only probes strictly below it); clientv3 restart-at-CompactRevision terminates.open0bugNULLNULL2026-07-17T13:18:41ZEugene Blikhbigbes@gmail.com2026-07-17T13:18:41ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-w5r3a127352109f5847f90475b493f71b010fa77a962ab2ab7fd469ed01e25e9984iproto-impl: etcd_election_campaign vs Election.CampaignWrap the existing handler election.campaign (already invoked by gRPC/JSON via defs.lua route v3electionpb.Election.Campaign) as a Lua global etcd_election_campaign callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_election_campaign', wrap('election', 'campaign')) under the appropriate service block; append 'etcd_election_campaign' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Election.Campaign returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83.etcd_election_campaign is callable from net.box:call('etcd_election_campaign', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply.Epic tarantool-etcd-w8q. Handler at app/etcd/election.lua.open3taskNULLNULL2026-05-19T14:54:43ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:36ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-w6za1dfa11cf641fccae10ce051a21c5eaca457020ec09cb52c85f4e5cc8d0062c1iproto-verify: etcd_election_campaign vs Election.CampaignAdd Lua testkit verification for etcd_election_campaign (mirrors gRPC v3electionpb.Election.Campaign). Same test pattern as verify-only tasks: positive path plus at least one error path, response shape matches gRPC, error codes from app/etcd/errors.lua. For UNIMPLEMENTED-by-design endpoints (Cluster.MemberAdd/Remove/Update/Promote), assert the wrapper returns the canonical UNIMPLEMENTED error consistent with gRPC rather than skipping. Goes in test/iproto_parity_test.lua under group iproto/parity/election.Epic tarantool-etcd-w8q. Blocked on the matching impl issue.open3taskNULLNULL2026-05-19T14:54:44ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:37ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL1
tarantool-etcd-w8q472222f8e9e50041b82f23593de29308c6f31d0649b21012f82ea3df9f38020ciproto parity for the gRPC surfaceBring app/etcd/api.lua iproto CALL surface to functional parity with the gRPC surface in app/etcd/pb/defs.lua. 32 already-implemented endpoints need Lua verification tests; 16 missing endpoints (Cluster, Maintenance, Election) need both implementation wrappers and tests. Out of scope: Watch and LeaseKeepAlive bidi-streams, Maintenance.Snapshot/Downgrade. For UNIMPLEMENTED-by-design endpoints, wrappers are created anyway and propagate the same UNIMPLEMENTED error so the surface is uniform.Every gRPC method in defs.lua (excluding streaming divergences) has a matching etcd_* iproto wrapper. Every wrapper has a Lua testkit test in test/iproto_parity_test.lua asserting response shape and error mapping match gRPC. test/run.lua group iproto/parity passes.open2epicNULLNULL2026-05-19T14:53:51ZEugene Blikhbigbes@gmail.com2026-05-19T14:53:51ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-wgx5a455111b3144aca15dfc7df9cce92515e6000d9cd0fa1e761c4ba493c83082fiproto-verify: etcd_authenticate vs Auth.AuthenticateAdd Lua testkit verification for etcd_authenticate (mirrors gRPC Auth.Authenticate). Successful path: assert response shape matches the gRPC response message after MsgPack round-trip. Error path: at least one error case asserting {code, message} matches app/etcd/errors.lua catalog. Exercise at least one non-default option if applicable. Goes in test/iproto_parity_test.lua under group iproto/parity/auth. Call shape: local resp = etcd_authenticate(req).Verification-only. Wrapper exists at app/etcd/api.lua. Epic tarantool-etcd-w8q.open3taskNULLNULL2026-05-19T14:54:05ZEugene Blikhbigbes@gmail.com2026-05-19T14:54:05ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-wqna7a2759a10e1b49b4c98ebf2bcc0979aff638f3764809fdee4605d8441e1adb6iproto-impl: etcd_maintenance_alarm vs Maintenance.AlarmWrap the existing handler maintenance.alarm (already invoked by gRPC/JSON via defs.lua route etcdserverpb.Maintenance.Alarm) as a Lua global etcd_maintenance_alarm callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_maintenance_alarm', wrap('maintenance', 'alarm')) under the appropriate service block; append 'etcd_maintenance_alarm' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Maintenance.Alarm returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83.etcd_maintenance_alarm is callable from net.box:call('etcd_maintenance_alarm', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply.Epic tarantool-etcd-w8q. Handler at app/etcd/maintenance.lua.open3taskNULLNULL2026-05-19T14:54:30ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:36ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-ymm5abccb539c482913cabd242acf2b32afd6eb182fda57781a502ac4411d704a0diproto-impl: etcd_maintenance_move_leader vs Maintenance.MoveLeaderWrap the existing handler maintenance.move_leader (already invoked by gRPC/JSON via defs.lua route etcdserverpb.Maintenance.MoveLeader) as a Lua global etcd_maintenance_move_leader callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_maintenance_move_leader', wrap('maintenance', 'move_leader')) under the appropriate service block; append 'etcd_maintenance_move_leader' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Maintenance.MoveLeader returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83.etcd_maintenance_move_leader is callable from net.box:call('etcd_maintenance_move_leader', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply.Epic tarantool-etcd-w8q. Handler at app/etcd/maintenance.lua.open3taskNULLNULL2026-05-19T14:54:41ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:36ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-zc747699b40eacc700cd65c09da563573fbd2ac63d67fd9ba709b6178394119ad21CLI utility: migrate existing config/data into tarantool-etcdProvide a configuration migration utility that moves an existing etcd configuration (and KV data) into a tarantool-etcd store, so users can adopt tarantool-etcd without hand-copying keys. Should cover the Tarantool EE centralized-config flow demonstrated in examples/tarantool-ee-self-hosted-etcd and examples/tarantool-ee-config: read config from a source (local cluster-config.yaml or a real etcd endpoint) and write it under the configured prefix into tarantool-etcd over the /v3 wire. Scope to nail down: source modes (file vs live etcd), key-range copy semantics, prefix handling, dry-run/diff output, and idempotency on re-run.open2featureNULLNULL2026-05-20T14:33:31ZEugene Blikhbigbes@gmail.com2026-05-20T14:33:31ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-zkg72a34c0ab42f90eede7cbbfd3ec5f7ef4f98cdbb5232099ad2926aaa87a61111Learner: conformance — join real etcd cluster, assert log replicationEnd-to-end test in tests/conformance: SpawnEtcd (or a small etcd cluster), MemberAddAsLearner the tarantool node with its advertise_peer_url, wait for it to become a healthy learner, then: (1) Put on the real etcd leader, assert it appears via Range on the tarantool learner with the SAME mod_revision; (2) assert MemberList on the cluster shows the tarantool node as IsLearner=true; (3) lease grant + key-with-lease on the leader replicates and the learner reflects lease attachment; (4) auth enable on the leader propagates; (5) force a snapshot (lower --snapshot-count, churn keys past compaction) and assert the learner recovers via snapshot receive. Use the harness, never tt start (memory feedback_use_harness_not_tt).This is the correctness oracle for the whole epic — each phase above is unverifiable in isolation against real etcd without it. Gate behind a build tag / env since it boots a foreign etcd cluster.open2taskNULLNULL2026-05-20T06:45:18ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:37ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL1
tarantool-etcd-zwg5f1f8ad7dadfa6be0ecaa0dc1d71f690076e930bc5073feb538b71faf1f2d7d6iproto-verify: etcd_user_get vs Auth.UserGetAdd Lua testkit verification for etcd_user_get (mirrors gRPC Auth.UserGet). Successful path: assert response shape matches the gRPC response message after MsgPack round-trip. Error path: at least one error case asserting {code, message} matches app/etcd/errors.lua catalog. Exercise at least one non-default option if applicable. Goes in test/iproto_parity_test.lua under group iproto/parity/auth. Call shape: local resp = etcd_user_get(req).Verification-only. Wrapper exists at app/etcd/api.lua. Epic tarantool-etcd-w8q.open3taskNULLNULL2026-05-19T14:54:08ZEugene Blikhbigbes@gmail.com2026-05-19T14:54:08ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL0
tarantool-etcd-zwsb99aec7b0025f26f4e59b5bf3e6287ee00f117d8f119775c13dcfbb23132d00eReplica route: revision alignment with upstreamIn replica mode the local global revision sequence (box.sequence in revision.lua) must mirror upstream's revisions exactly so that Range/Watch reads return etcd-consistent create_revision/mod_revision/header.revision. Work: - While replica mode is active, the local revision allocator must NOT mint its own revisions for replicated writes; it must set revision = the upstream mod_revision carried by each Range/Watch event. - Decide handling of the box.sequence: drive it to upstream's current revision (set, not next()) as events apply, or bypass it entirely for replicated mutations via the *_internal write paths. - Ensure header.revision returned to local clients equals the latest synced upstream revision, and compact_revision tracks upstream compaction. This is cross-cutting with bootstrap + watch-tail; it defines the invariant both must uphold.revision.lua owns the box.sequence. Replicated writes likely go through put_internal/delete_range_internal with an explicit revision arg to avoid double-allocation.open2taskNULLNULL2026-05-20T06:17:55ZEugene Blikhbigbes@gmail.com2026-07-17T14:27:37ZNULLNULL0NULLNULLNULL000<binary>0NULLNULLNULL0NULL1