11const { MongoClient, ObjectId } = require ( "mongodb" ) ;
2- const moment = require ( 'moment' )
3- const Command = require ( '../../Command' ) ;
4- const cmd = require ( './base.cmd' ) ;
5- const { objectIdFromDate, defaultCollections } = require ( './utils' )
6-
7- const offloadToCollection = async function ( sourceDBObj , collection , targetDB , cutoffDate ) {
8- const sourceCollectionObj = sourceDBObj . collection ( collection )
9- const targetCollection = `archive-${ collection } `
10-
11- const cutoffDateObj = moment ( cutoffDate )
12- . add ( 1 , 'days' )
13- . startOf ( "day" )
14-
15- if ( ! cutoffDateObj . isValid ( ) ) {
16- throw new Error ( 'please enter a valid date in ISO 8601 format' )
2+ const moment = require ( "moment" ) ;
3+ const semver = require ( "semver" ) ;
4+ const Command = require ( "../../Command" ) ;
5+ const cmd = require ( "./base.cmd" ) ;
6+ const {
7+ objectIdFromDate,
8+ defaultCollections,
9+ findMinLog,
10+ checkCursorState,
11+ createRangeStr,
12+ getMinDate,
13+ } = require ( "./utils" ) ;
14+
15+ const MERGE_RELEASE_VERSION = "4.2.0" ;
16+
17+ const offloadToCollection = async function (
18+ sourceDBObj ,
19+ collection ,
20+ targetDB ,
21+ cutoffDate
22+ ) {
23+ const sourceCollectionObj = sourceDBObj . collection ( collection ) ;
24+
25+ const cutoffDateObj = moment ( cutoffDate ) . add ( 1 , "days" ) . startOf ( "day" ) ;
26+
27+ if ( ! cutoffDateObj . isValid ( ) ) {
28+ throw new Error ( "please enter a valid date in ISO 8601 format" ) ;
1729 }
1830
19- const cutoffDateId = objectIdFromDate ( cutoffDateObj . toDate ( ) )
31+ const cutoffDateId = objectIdFromDate ( cutoffDateObj . toDate ( ) ) ;
2032
21- var result = sourceCollectionObj . aggregate ( [
22- { $match : { _id : { $lte : ObjectId ( cutoffDateId ) } } } ,
23- { $merge : {
24- into : { db : targetDB , coll : targetCollection } ,
25- on : "_id" ,
26- whenMatched : "keepExisting" ,
27- whenNotMatched : "insert"
28- } }
29- ] )
30-
31- await result . toArray ( )
32-
33- if ( ! result . cursorState . killed ) {
34- console . info ( `logs from '${ collection } ' were archived to '${ targetCollection } '` )
35- await sourceCollectionObj . deleteMany ( { _id : { $lte : ObjectId ( cutoffDateId ) } } )
33+ const minLog = await findMinLog (
34+ sourceCollectionObj ,
35+ cutoffDateId ,
36+ collection
37+ ) ;
38+
39+ if ( ! minLog ) {
40+ console . info (
41+ `No logs to archive in collection ${ collection } from the given date.`
42+ ) ;
43+ return ;
3644 }
37- else {
38- console . error ( "Cursor error. Archiving operation may not be completed." )
39- console . error ( "The old logs were not deleted from the source collection." )
45+
46+ const lowerBound = getMinDate ( minLog ) ;
47+ const upperBound = moment ( cutoffDateObj ) ;
48+ const targetCollection = `${ createRangeStr (
49+ lowerBound ,
50+ upperBound ,
51+ collection
52+ ) } -archive`;
53+
54+ const mongoInfo = await sourceDBObj . admin ( ) . serverInfo ( ) ;
55+ const mongoServerVersion = await mongoInfo . version ;
56+
57+ if ( semver . lte ( MERGE_RELEASE_VERSION , mongoServerVersion ) ) {
58+ await archiveWithMerge (
59+ sourceCollectionObj ,
60+ cutoffDateId ,
61+ targetDB ,
62+ targetCollection ,
63+ collection
64+ ) ;
65+ } else {
66+ await archiveWithOut (
67+ sourceCollectionObj ,
68+ cutoffDateId ,
69+ targetCollection ,
70+ collection
71+ ) ;
4072 }
41- }
73+ } ;
74+ ///////////////////////////////////////////////////////////////////////////////////
75+ const archiveWithMerge = async function (
76+ sourceCollectionObj ,
77+ cutoffDateId ,
78+ targetDB ,
79+ targetCollection ,
80+ collection
81+ ) {
82+ var result = sourceCollectionObj . aggregate ( [
83+ { $match : { _id : { $lte : ObjectId ( cutoffDateId ) } } } ,
84+ {
85+ $merge : {
86+ into : { db : targetDB , coll : targetCollection } ,
87+ on : "_id" ,
88+ whenMatched : "keepExisting" ,
89+ whenNotMatched : "insert" ,
90+ } ,
91+ } ,
92+ ] ) ;
4293
94+ await result . toArray ( ) ;
95+
96+ checkCursorState ( result , collection , targetCollection ) ;
97+
98+ await sourceCollectionObj . deleteMany ( {
99+ _id : { $lte : ObjectId ( cutoffDateId ) } ,
100+ } ) ;
101+ } ;
102+ ///////////////////////////////////////////////////////////////////////////////////
103+ const archiveWithOut = async function (
104+ sourceCollectionObj ,
105+ cutoffDateId ,
106+ targetCollection ,
107+ collection
108+ ) {
109+ var result = sourceCollectionObj . aggregate ( [
110+ { $match : { _id : { $lte : ObjectId ( cutoffDateId ) } } } ,
111+ { $out : targetCollection } ,
112+ ] ) ;
113+
114+ await result . toArray ( ) ;
115+
116+ checkCursorState ( result , collection , targetCollection ) ;
117+
118+ await sourceCollectionObj . deleteMany ( {
119+ _id : { $lte : ObjectId ( cutoffDateId ) } ,
120+ } ) ;
121+ } ;
122+ ///////////////////////////////////////////////////////////////////////////////////
43123const command = new Command ( {
44- command : 'offload-to-collection' ,
45- parent : cmd ,
46- description : 'Archiving logs from one or more source collections to target collections.' ,
47- webDocs : {
48- category : 'Logs' ,
49- title : 'Offload To Collection' ,
50- } ,
51- builder : yargs => yargs
52- . option ( 'targetDB' , {
53- alias : 'tdb' ,
54- describe : "Target database name, if none inserted, db will be defined as target." ,
55- type : "string" ,
56- } )
57- . option ( 'cutoffDate' , {
58- alias : "cod" ,
59- describe :
60- "The date in ISO format (yyyy-MM-dd) from which logs will be archived (going backwards, including logs from that day)." ,
61- demandOption : true ,
62- type : "string" ,
63- } )
64- . example ( 'codefresh offline-logs offload-to-collection --uri "mongodb://192.168.99.100:27017" --db logs --c logs foo --cod "2021-07-08" ' ) ,
65- handler : async ( argv ) => {
66- const {
67- uri,
68- db,
69- targetDB,
70- cutoffDate,
71- } = argv
72-
73- const client = new MongoClient ( uri ) ;
74- try {
75- await client . connect ( )
76- const failedCollections = [ ] ;
77- const sourceDBObj = client . db ( db ) ;
78- const promises = defaultCollections . map ( async ( collection ) => {
79- try {
80- await offloadToCollection ( sourceDBObj , collection , targetDB || db , cutoffDate ) ;
81- } catch ( error ) {
82- failedCollections . push ( collection )
83- }
84- } )
85- await Promise . all ( promises )
86-
87- if ( failedCollections . length ) {
88- throw new Error ( `failed to offload from collections: ${ failedCollections . join ( ', ' ) } ` )
124+ command : "offload-to-collection" ,
125+ parent : cmd ,
126+ description :
127+ "Archiving logs from one or more source collections to target collections." ,
128+ webDocs : {
129+ category : "Logs" ,
130+ title : "Offload To Collection" ,
131+ } ,
132+ builder : ( yargs ) =>
133+ yargs
134+ . option ( "targetDB" , {
135+ alias : "tdb" ,
136+ describe :
137+ "This option is available only for mongodb version 4.2 and up. for older versions it will be ignored. \
138+ Target database name, if none inserted, db will be defined as target." ,
139+ type : "string" ,
140+ } )
141+ . option ( "cutoffDate" , {
142+ alias : "cod" ,
143+ describe :
144+ "The date in ISO format (yyyy-MM-dd) from which logs will be archived (going backwards, including logs from that day)." ,
145+ demandOption : true ,
146+ type : "string" ,
147+ } )
148+ . example (
149+ 'codefresh offline-logs offload-to-collection --uri "mongodb://192.168.99.100:27017" --db logs --cod "2021-07-08" '
150+ ) ,
151+ handler : async ( argv ) => {
152+ const { uri, db, targetDB, cutoffDate } = argv ;
153+
154+ const client = new MongoClient ( uri , { useUnifiedTopology : true } ) ;
155+ try {
156+ await client . connect ( ) ;
157+ const failedCollections = [ ] ;
158+ const errors = [ ] ;
159+ const sourceDBObj = client . db ( db ) ;
160+ const promises = defaultCollections . map ( async ( collection ) => {
161+ try {
162+ await offloadToCollection (
163+ sourceDBObj ,
164+ collection ,
165+ targetDB || db ,
166+ cutoffDate
167+ ) ;
168+ } catch ( error ) {
169+ failedCollections . push ( collection ) ;
170+ errors . push ( error ) ;
89171 }
90- } finally {
91- client . close ( ) ;
172+ } ) ;
173+ await Promise . all ( promises ) ;
174+
175+ if ( failedCollections . length ) {
176+ throw new Error (
177+ `failed to offload from collections: ${ failedCollections . join (
178+ ", "
179+ ) } . ${ errors . join ( ", " ) } `
180+ ) ;
92181 }
93- } ,
94- } )
182+ } finally {
183+ client . close ( ) ;
184+ }
185+ } ,
186+ } ) ;
95187
96- module . exports = command ;
188+ module . exports = command ;
0 commit comments