@@ -1129,6 +1129,10 @@ struct redisCommand redisCommandTable[] = {
1129
1129
{"xslaveof" ,xslaveofCommand ,3 ,
1130
1130
"admin no-script ok-stale" ,
1131
1131
0 ,NULL ,0 ,0 ,0 ,0 ,0 ,0 },
1132
+
1133
+ {"import" ,importCommand ,-2 ,
1134
+ "admin use-memory ok-loading fast @dangerous" ,
1135
+ 0 ,NULL ,0 ,0 ,0 ,0 ,0 ,0 },
1132
1136
};
1133
1137
#endif
1134
1138
@@ -1896,7 +1900,7 @@ void clientsCron(void) {
1896
1900
void databasesCron (void ) {
1897
1901
/* Expire keys by random sampling. Not required for slaves
1898
1902
* as master will synthesize DELs for us. */
1899
- if (server .active_expire_enabled ) {
1903
+ if (server .active_expire_enabled && ! isImportingExpireDisabled () ) {
1900
1904
if (iAmMaster ()) {
1901
1905
activeExpireCycle (ACTIVE_EXPIRE_CYCLE_SLOW );
1902
1906
} else {
@@ -2523,7 +2527,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
2523
2527
2524
2528
/* Run a fast expire cycle (the called function will return
2525
2529
* ASAP if a fast cycle is not needed). */
2526
- if (server .active_expire_enabled && server .masterhost == NULL )
2530
+ if (server .active_expire_enabled && ! isImportingExpireDisabled () && server .masterhost == NULL )
2527
2531
activeExpireCycle (ACTIVE_EXPIRE_CYCLE_FAST );
2528
2532
2529
2533
/* Unblock all the clients blocked for synchronous replication
@@ -3548,6 +3552,8 @@ void InitServerLast() {
3548
3552
initThreadedIO ();
3549
3553
set_jemalloc_bg_thread (server .jemalloc_bg_thread );
3550
3554
server .initial_memory_usage = zmalloc_used_memory ();
3555
+ server .importing_expire_enabled = 1 ;
3556
+ server .importing_end_time = 0 ;
3551
3557
}
3552
3558
3553
3559
/* Parse the flags string description 'strflags' and set them to the
@@ -4823,6 +4829,115 @@ NULL
4823
4829
}
4824
4830
}
4825
4831
4832
+ /* The import command, only recommended to use in target server
4833
+ * during keys migration. It means time-based importing mode in server.
4834
+ * As default, Some action will not be executed during this mode,
4835
+ * which are:
4836
+ * 1. expire,
4837
+
4838
+ * import <subcommand> [[arg] [value] [opt] ...]
4839
+
4840
+ * subcommand supported:
4841
+ * import start [ttl]
4842
+ * import end
4843
+ * imoort status
4844
+ * import set ((ttl <seconds>) | ( expire < 1|0 > ) )
4845
+ * import get (ttl | expire)
4846
+ *
4847
+ * */
4848
+
4849
+ static inline int isImporting () {
4850
+ return server .importing_end_time >= server .mstime ;
4851
+ }
4852
+
4853
+ void importCommand (client * c ) {
4854
+ if (c -> argc == 2 && !strcasecmp (c -> argv [1 ]-> ptr ,"help" )) {
4855
+ const char * help [] = {
4856
+ "start [ttl]" ,
4857
+ " importing mode start with seconds of ttl, default ttl as 60s," ,
4858
+ " expire is default disabled." ,
4859
+ "end" ,
4860
+ " importing mode end." ,
4861
+ "status" ,
4862
+ " return 1 in importing mode, return 0 if not." ,
4863
+ "set ((ttl <seconds>) | ( expire < 1|0 > > ) )" ,
4864
+ " set some options." ,
4865
+ "get (ttl | expire)" ,
4866
+ " get some options." ,
4867
+ NULL };
4868
+ addReplyHelp (c , help );
4869
+ } else if ((c -> argc == 2 || c -> argc == 3 ) && !strcasecmp (c -> argv [1 ]-> ptr ,"start" )) {
4870
+ if (!isImporting ()) {
4871
+ /* if importing mode already off, sub-status will not be inherited,
4872
+ * which is reset to default value.
4873
+ */
4874
+ server .importing_expire_enabled = 0 ;
4875
+ }
4876
+
4877
+ long long ttl ;
4878
+ if (c -> argc == 2 ) {
4879
+ ttl = 3600 ;
4880
+ } else if (c -> argc == 3 ) {
4881
+ if (getLongLongFromObjectOrReply (c , c -> argv [2 ], & ttl , NULL ) != C_OK ) {
4882
+ return ;
4883
+ }
4884
+ }
4885
+ server .importing_end_time = mstime () + ttl * 1000 ;
4886
+ addReply (c ,shared .ok );
4887
+ } else if (c -> argc == 2 && !strcasecmp (c -> argv [1 ]-> ptr ,"end" )) {
4888
+ if (!isImporting ()) {
4889
+ addReplyError (c ,"Importing mode already ended." );
4890
+ return ;
4891
+ }
4892
+ server .importing_end_time = -1 ;
4893
+ addReply (c ,shared .ok );
4894
+ } else if (c -> argc == 2 && !strcasecmp (c -> argv [1 ]-> ptr ,"status" )) {
4895
+ if (isImporting ()) {
4896
+ addReplyLongLong (c , 1 );
4897
+ } else {
4898
+ addReplyLongLong (c , 0 );
4899
+ }
4900
+ } else if (c -> argc == 3 && !strcasecmp (c -> argv [1 ]-> ptr ,"get" )) {
4901
+ if (!isImporting ()) {
4902
+ addReplyError (c ,"IMPORT GET must be called in importing mode." );
4903
+ return ;
4904
+ }
4905
+
4906
+ if (!strcasecmp (c -> argv [2 ]-> ptr ,"ttl" )) {
4907
+ addReplyLongLong (c , (server .importing_end_time - mstime ()) / 1000 );
4908
+ } else if (!strcasecmp (c -> argv [2 ]-> ptr ,"expire" )) {
4909
+ addReplyLongLong (c , (long long )server .importing_expire_enabled );
4910
+ } else {
4911
+ addReplyError (c ,"Invalid option." );
4912
+ }
4913
+ } else if (c -> argc == 4 && !strcasecmp (c -> argv [1 ]-> ptr ,"set" )) {
4914
+ if (!isImporting ()) {
4915
+ addReplyError (c ,"IMPORT SET must be called in importing mode." );
4916
+ return ;
4917
+ }
4918
+
4919
+ if (!strcasecmp (c -> argv [2 ]-> ptr ,"ttl" )) {
4920
+ long long ttl ;
4921
+ if (getLongLongFromObjectOrReply (c , c -> argv [3 ], & ttl , NULL ) != C_OK ) {
4922
+ return ;
4923
+ }
4924
+ server .importing_end_time = mstime () + ttl * 1000 ;
4925
+ addReply (c ,shared .ok );
4926
+ } else if (!strcasecmp (c -> argv [2 ]-> ptr ,"expire" )) {
4927
+ server .importing_expire_enabled = (atoi (c -> argv [3 ]-> ptr ) != 0 );
4928
+ addReply (c ,shared .ok );
4929
+ } else {
4930
+ addReplyError (c ,"Invalid option." );
4931
+ }
4932
+ } else {
4933
+ addReplyError (c ,"Invalid subcommand." );
4934
+ }
4935
+ }
4936
+
4937
+ int isImportingExpireDisabled () {
4938
+ return (isImporting () && (server .importing_expire_enabled == 0 ));
4939
+ }
4940
+
4826
4941
/* Convert an amount of bytes into a human readable string in the form
4827
4942
* of 100B, 2G, 100M, 4K, and so forth. */
4828
4943
void bytesToHuman (char * s , unsigned long long n ) {
@@ -5288,6 +5403,8 @@ sds genRedisInfoString(const char *section) {
5288
5403
atomicGet (server .stat_net_input_bytes , stat_net_input_bytes );
5289
5404
atomicGet (server .stat_net_output_bytes , stat_net_output_bytes );
5290
5405
5406
+ long long importing_ttl = isImporting ()? (server .importing_end_time - mstime ()):0 ;
5407
+
5291
5408
if (sections ++ ) info = sdscat (info ,"\r\n" );
5292
5409
info = sdscatprintf (info ,
5293
5410
"# Stats\r\n"
@@ -5328,7 +5445,8 @@ sds genRedisInfoString(const char *section) {
5328
5445
"total_reads_processed:%lld\r\n"
5329
5446
"total_writes_processed:%lld\r\n"
5330
5447
"io_threaded_reads_processed:%lld\r\n"
5331
- "io_threaded_writes_processed:%lld\r\n" ,
5448
+ "io_threaded_writes_processed:%lld\r\n"
5449
+ "importing:status=%d,ttl=%lld,expire=%d\r\n" ,
5332
5450
server .stat_numconnections ,
5333
5451
server .stat_numcommands ,
5334
5452
getInstantaneousMetric (STATS_METRIC_COMMAND ),
@@ -5366,7 +5484,8 @@ sds genRedisInfoString(const char *section) {
5366
5484
stat_total_reads_processed ,
5367
5485
stat_total_writes_processed ,
5368
5486
server .stat_io_reads_processed ,
5369
- server .stat_io_writes_processed );
5487
+ server .stat_io_writes_processed ,
5488
+ isImporting (),importing_ttl ,server .importing_expire_enabled );
5370
5489
}
5371
5490
5372
5491
/* Replication */
0 commit comments